package dao

import (
	"alan-snippet/internal/dto"
	"alan-snippet/internal/models"
	"errors"
)

func CreateSnippet(snippet *models.Snippet) error {
	return DB.Create(snippet).Error
}

func ListSnippet(listDTO dto.ListSnippetsRequestDTO) ([]models.Snippet, int64, error) {
	var snippets []models.Snippet
	var total int64

	// 1. 构建基础查询
	// 我们从 Snippet 模型开始，并预加载（Preload）相关的 Tags。
	// Preload("Tags") 是一个 GORM 的强大功能，它会高效地执行另一条查询，
	// 以获取所有找到的 Snippets 的关联标签，避免了 N+1 查询问题。
	query := DB.Model(&models.Snippet{}).Preload("Tags")

	// 2. 如果提供了 tagName，则添加过滤条件
	if listDTO.Tag != "" {
		// 这是 GORM 中进行多对多查询的关键！
		// 我们需要 JOIN 中间表和目标表，然后在外键上进行筛选。
		query = query.Joins("JOIN snippet_tags ON snippets.id = snippet_tags.snippet_id").
			Joins("JOIN tags ON tags.id = snippet_tags.tag_id").
			Where("tags.name = ?", listDTO.Tag)
	}

	// 3. 首先，在不应用分页的情况下，计算总数
	// 我们在应用 `LIMIT` 和 `OFFSET` 之前执行 `Count`，这样就能得到满足条件的总记录数。
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 4. 然后，添加分页和排序条件
	// Offset 计算：(页码 - 1) * 每页数量
	offset := (listDTO.Page - 1) * listDTO.Size
	// 按创建时间降序排序，最新的在前面
	query = query.Offset(offset).Limit(listDTO.Size).Order("created_at DESC")

	// 5. 执行最终查询，获取当前页的数据
	if err := query.Find(&snippets).Error; err != nil {
		return nil, 0, err
	}

	return snippets, total, nil
}

func GetSnippetById(id int) (*models.Snippet, error) {
	var snippet models.Snippet
	err := DB.Preload("Tags").Where("id=?", id).First(&snippet).Error
	if err != nil {
		return nil, err
	}
	return &snippet, nil
}

func GetPopularSnippets(ids []string) ([]*models.Snippet, error) {
	var snippets []*models.Snippet

	// 一定要preload，不要查不出来外键元素
	err := DB.Preload("Tags").Where("id IN ?", ids).Find(&snippets).Error
	if err != nil {
		return nil, err
	}
	return snippets, nil
}

func UpdateSnippet(id int, snippet *models.Snippet) (*models.Snippet, error) {
	res := DB.Model(snippet).Where("id=?", id).Updates(snippet)
	if res.Error != nil {
		return nil, res.Error
	}
	if res.RowsAffected == 0 {
		return nil, errors.New("更新失败")
	}
	res.First(snippet)
	return snippet, nil
}

func DeleteSnippet(id int) error {
	if err := DB.Delete(&models.Snippet{}, id).Error; err != nil {
		return err
	}
	return nil
}
